from mainwindow import Ui_MainWindow
from PyQt5.QtWidgets import QMainWindow, QFileDialog, QMessageBox, QInputDialog
from PyQt5.QtCore import QTimer, QDateTime, QDate, QTime, pyqtSignal
from PyQt5.QtGui import QPixmap
from cameravideo import camera
import requests
import json
import cv2
import base64
from detect import detect_thread
from adduserwindow import adduserwindow
from deluserwindow import deluserwindow

'''
子类，继承UI_mainwindow与qmainwindow
Ui_MainWindow
    包含界面的设计，窗口中的窗口部件
QMainWindow
    包含整个界面窗口，窗口的操作
mywindow
    完整的窗口类
'''

class mywindow(Ui_MainWindow, QMainWindow):
    detect_data_signal = pyqtSignal(bytes)

    camera_status = False

    def __init__(self):
        super(mywindow, self).__init__()
        self.setupUi(self)   # 创建界面内容
        self.label.setScaledContents(True)
        self.label.setPixmap(QPixmap("./3.jpg"))

        # 创建一个定时器对象
        self.datetime = QTimer(self)
        # 启动获取系统/日期时间的定时器,定时时间为500毫秒，500毫秒产生一次信号
        self.datetime.start(500)
        # 关联时间/日期的定时器信号与槽函数
        self .datetime.timeout.connect(self.date_time)

        # 创建窗口就应该完成访问令牌的申请（获取）
        self.get_accesstoken()

        '''
        # 信号与槽的关联
        # self.actionopen:指定对象
        # connect:关联（槽函数）
        # self.on_actionopen:关联函数是什么
        '''
        # 开启摄像头
        self.actionopen.triggered.connect(self.on_actionopen)
        # 关闭摄像头
        self.actionclose.triggered.connect(self.on_actionclose)

        # 添加用户组信号槽
        self.actionaddgroup.triggered.connect(self.add_group)
        # 删除用户组信号槽
        self.actiondeletegroup.triggered.connect(self.del_group)
        # 查询用户组信号槽
        self.actiongetlist.triggered.connect(self.getgrouplist)
        # 添加用户
        self.actionadduser.triggered.connect(self.add_user)
        # 删除用户
        self.actiondeleteuser.triggered.connect(self.del_user)

        # self.pushButton.clicked.connect(self.get_accesstoken)
        # self.pushButton_2.clicked.connect(self.get_face)
        # self.pushButton.clicked.connect(self.create_thread)

    # 函数功能：获取日期时间,添加到对应编辑器
    def date_time(self):
        # 获取日期
        date = QDate.currentDate()
        # print(date)
        # self.dateEdit.setDate(date)
        self.label_3.setText("日期:"+date.toString())
        # 获取时间
        time = QTime.currentTime()
        # print(time)
        # self.timeEdit.setTime(time)
        self.label_2.setText("时间:"+time.toString())
        # 获取日期时间
        # datetime = QDateTime.currentDateTime()
        # print(datetime)

    '''
    信号槽功能：
        当某个组件设计了信号槽功能时，当信号产生，会主动调用槽函数去完成对应的一个功能
        信号：当以某种特定的操作，操作到这个组件时，就会主动产生对应操作的信号
    '''
    def on_actionopen(self):
        # 启动摄像头
        self.cameravideo = camera()
        self.camera_status = True
        # 启动定时器，进行定时，每隔多长时间进行一次获取摄像头数据进行显示
        self.timeshow = QTimer(self)
        self.timeshow.start(50)
        # 50毫秒的定时器启动，每到50毫秒就会产生一个信号timeout
        self.timeshow.timeout.connect(lambda: self.show_cameradata())
        # timeshow定时器用作显示画面

        # 创建检测线程
        self.create_thread()

        # 当开启检测启动时，创建定时器500毫秒，用作获取要检测的画面
        self.facedetecttime = QTimer(self)
        self.facedetecttime.start(500)
        # self.facedetecttime.timeout.connect(self.get_face)
        self.facedetecttime.timeout.connect(lambda: self.get_cameradata())
        # facedetecttime 定时器设置检测画面获取
        # 关联一个窗口中的信号与创建的线程中的函数
        self.detect_data_signal.connect(self.detectThread.get_base64)

        self.detectThread.transmit_data.connect(self.get_detectdata)
        self.detectThread.search_data.connect(self.get_search_data)

    # 创建线程完成检测
    def create_thread(self):
        self.detectThread = detect_thread(self.access_token)
        self.detectThread.start()

    def get_cameradata(self):
        # 摄像头获取画面
        camera_data = self.cameravideo.read_camera()
        # 把摄像头画面转化成图片，设置编码为base64编码格式数据
        _, enc = cv2.imencode('.jpg', camera_data)
        base64_image = base64.b64encode(enc.tobytes())
        # 产生信号，传递数据
        self.detect_data_signal.emit(bytes(base64_image))

    # 关闭摄像头操作
    def on_actionclose(self):
        # 关闭获取检测数据
        self.facedetecttime.stop()
        # self.timeshow.timeout.disconnect(lambda: self.show_cameradata())

        # 关闭检测线程
        self.detectThread.OK = False
        self.detectThread.quit()
        self.detectThread.wait()

        # 关闭定时器，不再去获取摄像头数据进行显示
        self.timeshow.stop()
        # self.timeshow.timeout.disconnect(lambda: self.show_cameradata())
        # 关闭摄像头
        self.cameravideo.close_camera()
        self.camera_status = False
        # 界面显示恢复到开始之前的画面
        # self.label.setText("摄像头画面显示")
        self.label.setPixmap(QPixmap("./3.jpg"))

    # 作为摄像头获取数据实时显示画面的功能
    # 只要能够不断的重复调用这个函数，不断的从摄像头获取数据进行显示
    # 可以通过信号，信号关联当前函数，只要信号产生，函数就会被调用
    # 信号需要不断的产生，可以通过定时器，定时时间到达就会产生信号
    def show_cameradata(self):
        # 获取摄像头数据，转换数据
        pic = self.cameravideo.camera_to_pic()
        # 显示数据，显示画面
        self.label.setPixmap(pic)

    # 获取访问令牌
    def get_accesstoken(self):
        # print("get_accesstoken")
        # host对象是字符串对象存储是授权的服务地址---获取accesstoken的地址
        host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=IVKgGrmQoSIY2UY8uFTW8cGf&client_secret=diRAlbrBIKuvploxLpPUkZ6RRPxFSQDE'
        # 发送网络请求  requests网络库
        # 使用get函数发送网络请求，参数为网络请求的地址，执行时会产生返回结果，结果就是请求的结果
        response = requests.get(host)
        if response:
            # print(response.json())
            # json.load(response)
            data = response.json()
            self.access_token = data.get('access_token')
            print(self.access_token)

    # 从新线程的人脸检测中获得的检测结果进行输出到窗口
    # 槽函数，获取检测数据
    def get_detectdata(self, data):
        if data['error_code'] != 0:
            self.plainTextEdit_2.setPlainText(data['error_msg'])
        # 判断是否解析成功
        elif data['error_msg'] == "SUCCESS":
            # 在data字典中，键为‘result’对应的值才是返回的检测结果
            # data['result']就是检测结果
            # 人脸数目
            self.plainTextEdit_2.clear()
            face_num = data['result']['face_num']
            if face_num == 0:
                print("no face")
                self.plainTextEdit_2.appendPlainText("当前没有人以及人脸出现")
                return
            else:
                self.plainTextEdit_2.appendPlainText("存在人脸")
            # 人脸信息: data['result']['face_list']是列表，每个数据就是一个人脸信息，需要取出每个人脸数据
            # 每个人脸信息:data['result']['face_list'][0~i]人脸信息字典
            for i in range(face_num):
                # 通过for循环，分别取出列表的每一个数据
                # data['result']['face_list'][i]，就是一个人脸信息的字典
                age = data['result']['face_list'][i]['age']
                beauty = data['result']['face_list'][i]['beauty']
                gender = data['result']['face_list'][i]['gender']['type']
                expression = data['result']['face_list'][i]['expression']['type']
                face_shape = data['result']['face_list'][i]['face_shape']['type']
                glasses = data['result']['face_list'][i]['glasses']['type']
                emotion = data['result']['face_list'][i]['emotion']['type']
                mask = data['result']['face_list'][i]['mask']['type']
                # 向窗口中添加文本，参数就是需要的文本信息
                self.plainTextEdit_2.appendPlainText(str(i + 1) + "学生信息")
                self.plainTextEdit_2.appendPlainText("年龄:" + str(age))
                self.plainTextEdit_2.appendPlainText("颜值:" + str(beauty))
                self.plainTextEdit_2.appendPlainText("性别:" + str(gender))
                self.plainTextEdit_2.appendPlainText("表情:" + str(expression))
                self.plainTextEdit_2.appendPlainText("脸型:" + str(face_shape))
                self.plainTextEdit_2.appendPlainText("眼镜:" + str(glasses))
                self.plainTextEdit_2.appendPlainText("情绪:" + str(emotion))
                if mask == 0:
                    mask = "否"
                else:
                    mask = "是"
                self.plainTextEdit_2.appendPlainText("口罩佩戴:" + str(mask))

    # 人脸识别结果信息显示
    def get_search_data(self, data):
        self.plainTextEdit.setPlainText(data)

    # 创建用户组
    def add_group(self):
        # 打开对话框,进行输出用户组
        group, ret = QInputDialog.getText(self, "添加用户组", "输入用户组(由数字、字母、下划线组成)")
        request_url = "https://aip.baidubce.com/rest/2.0/face/v3/faceset/group/add"

        params = {
            "group_id": group
        }
        access_token = self.access_token
        request_url = request_url + "?access_token=" + access_token
        headers = {'content-type': 'application/json'}
        response = requests.post(request_url, data=params, headers=headers)
        if response:
            message = response.json()
            if message['error_code'] == 0:
                QMessageBox.about(self, "用户组创建结果", "用户组创建成功")
            else:
                QMessageBox.about(self, "用户组创建结果", "用户组创建失败"+message['error_msg'])

    # 删除用户组
    def del_group(self):
        request_url = "https://aip.baidubce.com/rest/2.0/face/v3/faceset/group/delete"

        # 删除，需要知道存在哪些组
        list = self.getlist()
        group, ret = QInputDialog.getText(self, "存在的用户组", "用户组信息\n"+str(list['result']['group_id_list']))
        params = {
            "group_id": group  # 要删除的用户组的ID
        }
        access_token = self.access_token
        request_url = request_url + "?access_token=" + access_token
        headers = {'content-type': 'application/json'}
        response = requests.post(request_url, data=params, headers=headers)
        if response:
            data = response.json()
            if data['error_code'] == 0:
                QMessageBox.about(self, "用户组删除结果", "用户组删除成功")
            else:
                QMessageBox.about(self, "用户组删除结果", "用户组删除失败" + data['error_msg'])

    # 获取用户组信息
    def getlist(self):
        request_url = "https://aip.baidubce.com/rest/2.0/face/v3/faceset/group/getlist"

        params = {
            "start": 0,
            "length": 100
        }
        access_token = self.access_token
        request_url = request_url + "?access_token=" + access_token
        headers = {'content-type': 'application/json'}
        response = requests.post(request_url, data=params, headers=headers)
        if response:
            return response.json()

    # 获取用户组列表并显示
    def getgrouplist(self):
        list = self.getlist()
        str = ''
        for i in list['result']['group_id_list']:
            str = str + '\n' + i
        QMessageBox.about(self, "用户组列表", str)

    # 添加用户，人脸注册
    def add_user(self):
        request_url = "https://aip.baidubce.com/rest/2.0/face/v3/faceset/user/add"
        '''if self.cameravideo.capture.isOpened():
            QMessageBox.about(self, "摄像头状态", "摄像头已打开，正在进行签到，请关闭签到再添加用户")
            return'''
        if self.camera_status:
            QMessageBox.about(self, "摄像头状态", "摄像头已打开，正在进行签到，请关闭签到再添加用户")
            return
        list = self.getlist()
        # 创建一个窗口来选择这些内容
        window = adduserwindow(list['result']['group_id_list'], self)
        # 新创建的窗口，通过exec函数一直执行，阻塞执行，窗口不进行关闭，exec函数不会退出
        window_status = window.exec_()
        # 进行判断，判断是否是点击确定按钮进行关闭
        if window_status != 1:
            return
        # 请求参数中，需要获取人脸，转换人脸编码，添加的组ID，添加的用户ID，新用户ID的信息
        params = {
            "image": window.base64_image,  # 人脸图片
            "image_type": "BASE64",  # 人脸图片编码
            "group_id": window.group_id,  # 组ID
            "user_id": window.user_id,  # 新用户ID
            "user_info": 'name:'+window.msg_name+'\n'+'class:'+window.msg_class,  # 用户信息
        }
        access_token = self.access_token
        request_url = request_url + "?access_token=" + access_token
        headers = {'content-type': 'application/json'}
        response = requests.post(request_url, data=params, headers=headers)
        if response:
            data = response.json()
            if data['error_code'] == 0:
                QMessageBox.about(self, "人脸注册结果", "人脸注册成功")
            else:
                QMessageBox.about(self, "人脸注册结果", "人脸注册失败" + data['error_msg'])

    # 删除用户
    def del_user(self):

        # 查询用户人脸信息(face_token)
        # 获取用户组，选择
        list = self.getlist()
        # 创建一个窗口来选择这些内容
        window = deluserwindow(list['result']['group_id_list'], self.access_token, self)
        # 新创建的窗口，通过exec函数一直执行，阻塞执行，窗口不进行关闭，exec函数不会退出
        window_status = window.exec_()
        # 进行判断，判断是否是点击确定按钮进行关闭
        if window_status != 1:
            return